#pragma once

#include <deque>
#include <mutex>
#include <memory>
#include <condition_variable>

// 实现线程安全队列
template<class T, class Container = std::deque<T>>
class threadsafe_queue
{
    template<class U, class Sequence>
    friend bool operator==(const threadsafe_queue<U, Sequence>& x, const threadsafe_queue<U, Sequence>& y);

    template<class U, class Sequence>
    friend bool operator<(const threadsafe_queue<U, Container>& x, const threadsafe_queue<U, Sequence>& y);

public:
    typedef Container container_type;
    typedef typename Container::value_type value_type;
    typedef typename Container::size_type size_type;
    typedef typename Container::reference reference;
    typedef typename Container::const_reference const_reference;

protected:
    Container c;
    mutable std::mutex mutex;
    std::condition_variable cond;

public:
    threadsafe_queue() : c() {};
    explicit threadsafe_queue(const threadsafe_queue& other)
    {
        std::lock_guard<std::mutex> lk(other.mutex);
        c = other.c;
    }

    bool empty() const
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.empty();
    }

    void clear()
    {
        std::lock_guard<std::mutex> lk(mutex);
        c.clear();
    }

    size_t size() const
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.size();
    }

    reference front() 
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.front();
    }

    const_reference front() const
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.front();
    }

    reference back()
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.back();
    }

    const_reference back() const
    {
        std::lock_guard<std::mutex> lk(mutex);
        return c.back();
    }

    void push(const value_type& x)
    {
        std::lock_guard<std::mutex> lk(mutex);
        c.push_back(x);
        cond.notify_one();
    }

    void wait_and_pop(value_type& x)
    {
        std::unique_lock<std::mutex> lk(mutex);
        cond.wait(lk, [this]{ return !c.empty(); });
        x = c.front();
        c.pop_front();
    }

    std::shared_ptr<value_type> wait_and_pop()
    {
        std::unique_lock<std::mutex> lk(mutex);
        cond.wait(lk, [this]{ return !c.empty(); });
        std::shared_ptr<value_type> res(std::make_shared<value_type>(c.front()));
        c.pop_front();
        return res;
    }

    bool try_pop(value_type& x)
    {
        std::lock_guard<std::mutex> lk(mutex);
        if (c.empty()) return false;
        x = c.front();
        c.pop_front();
        return true;
    }

    std::shared_ptr<value_type> try_pop() 
    {
        std::lock_guard<std::mutex> lk(mutex);
        if (c.empty()) return std::shared_ptr<value_type>();
        std::shared_ptr<value_type> res(std::make_shared<value_type>(c.front()));
        c.pop_front();
        return res;
    }
};

template<class U, class Sequence>
bool operator== (const threadsafe_queue<U, Sequence>& x, const threadsafe_queue<U, Sequence>& y) {
    std::unique_lock<std::mutex> lk_x(x.mutex, std::defer_lock);
    std::unique_lock<std::mutex> lk_y(y.mutex, std::defer_lock);
    std::lock(lk_x, lk_y);
    return x.c == y.c;
}

template<class U, class Sequence>
bool operator< (const threadsafe_queue<U, Sequence>& x, const threadsafe_queue<U, Sequence>& y) {
    std::unique_lock<std::mutex> lk_x(x.mutex, std::defer_lock);
    std::unique_lock<std::mutex> lk_y(y.mutex, std::defer_lock);
    std::lock(lk_x, lk_y);
    return x.c < y.c;
}